package com.bolt.support.captcha.impl;

import com.bolt.common.convert.Convert;
import com.bolt.common.io.resource.ResourceUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.convention.exception.ServiceException;
import com.bolt.support.captcha.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public abstract class AbstractCaptchaService implements CaptchaService {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    protected CaptchaProperties properties;

    protected static int HAN_ZI_SIZE = 25;

    protected static String CACHE_CAPTCHA_KEY = "AUTH:CAPTCHA:%s";

    protected Font waterMarkFont;//水印字体

    protected String waterMark;//水印文字

    @Override
    public void init(CaptchaProperties config) {
        this.properties = config;
        this.waterMarkFont = loadFont(this.properties.getWaterFont());
        this.waterMark = StrUtil.emptyIf(this.properties.getWaterMark(), "我的水印");
    }

    protected void validateGet(CaptchaVO captchaVO) {
        // 无客户端身份标识，不限制
        if (StrUtil.isNotEmpty(captchaVO.getCaptchaId())) {
            String getKey = String.format(CACHE_CAPTCHA_KEY, captchaVO.getCaptchaId().concat("_GET"));
            String lockKey = String.format(CACHE_CAPTCHA_KEY, captchaVO.getCaptchaId().concat("_LOCK"));
            String failKey = String.format(CACHE_CAPTCHA_KEY, captchaVO.getCaptchaId().concat("_FAIL"));

            // 失败次数过多，锁定
            if (Objects.nonNull(CaptchaServiceFactory.getCache().get(lockKey))) {
                throw new ServiceException(CaptchaExceptionEnum.API_REQ_LOCK_GET_ERROR);
            }
            int getCnts = Convert.toInt(CaptchaServiceFactory.getCache().get(getKey), 0);
            if (Objects.isNull(getCnts)) {
                CaptchaServiceFactory.getCache().set(getKey, "1", 1);
                getCnts = 1;
            }
            CaptchaServiceFactory.getCache().increment(getKey, 1);
            // 1分钟内请求次数过多
            if (Long.valueOf(getCnts) > this.properties.getReqGetMinuteLimit()) {
                throw new ServiceException(CaptchaExceptionEnum.API_REQ_LIMIT_GET_ERROR);
            }
            // 失败次数验证
            int failCnts = Convert.toInt(CaptchaServiceFactory.getCache().get(failKey), 0);
            // 1分钟内失败5次
            if (Long.valueOf(failCnts) > this.properties.getReqGetLockLimit()) {
                // get接口锁定5分钟
                CaptchaServiceFactory.getCache().set(lockKey, "1", this.properties.getReqGetLockSeconds());
                throw new ServiceException(CaptchaExceptionEnum.API_REQ_LOCK_GET_ERROR);
            }
        }
    }

    protected void afterValidateFail(CaptchaVO captchaVO) {
        // 验证失败 分钟内计数
        String failKey = String.format(CACHE_CAPTCHA_KEY, captchaVO.getCaptchaId().concat("_FAIL"));
        CaptchaCacheService cacheService = CaptchaServiceFactory.getCache();
        if (!cacheService.exists(failKey)) {
            cacheService.set(failKey, "1", 60);
        }
        cacheService.increment(failKey, 1);
    }


    protected Font loadFont(String fontName) {
        try {
            if (fontName.toLowerCase().endsWith(".ttf") || fontName.toLowerCase().endsWith(".ttc")
                    || fontName.toLowerCase().endsWith(".otf")) {

               URL url = ResourceUtil.getResource("/captcha/fonts/" + fontName,this.getClass());
                logger.info("load font {},{}", fontName, url);
                return Font.createFont(Font.TRUETYPE_FONT, url.openStream())
                        .deriveFont(Font.BOLD, HAN_ZI_SIZE / 2);
            } else {
                return new Font(fontName, Font.BOLD, HAN_ZI_SIZE / 2);
            }

        } catch (Exception e) {
            logger.error("load font error:", e);
            throw new ServiceException(CaptchaExceptionEnum.API_CAPTCHA_ERROR);
        }
    }

    protected int getEnOrChLength(String s) {
        int enCount = 0;
        int chCount = 0;
        for (int i = 0; i < s.length(); i++) {
            int length = String.valueOf(s.charAt(i)).getBytes(StandardCharsets.UTF_8).length;
            if (length > 1) {
                chCount++;
            } else {
                enCount++;
            }
        }
        int chOffset = (HAN_ZI_SIZE / 2) * chCount + 5;
        int enOffset = enCount * 8;
        return chOffset + enOffset;
    }

}